/*
 * ServerConfiguration.java
 *
 * Created on June 12, 2007, 1:11 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.xeerkat.xmpp.server.tools;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.infoset.xml.Document;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Location;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.sax.SAXDocumentLoader;

/**
 *
 * @author alex
 */
public class ServerConfiguration
{
   public final static URI NAMESPACE = URI.create("http://www.xeerkat.org/Vocabulary/Admin/2007/1/0");
   public final static Name NM_SERVER = Name.create(NAMESPACE,"server");
   public final static Name NM_DATABASE = Name.create(NAMESPACE,"database");
   public final static Name NM_AUTOCONF = Name.create(NAMESPACE,"autoconf");
   public final static Name NM_INTERFACE = Name.create(NAMESPACE,"interface");
   public final static Name NM_HOST = Name.create(NAMESPACE,"host");
   public final static Name NM_KEYSTORE = Name.create(NAMESPACE,"keystore");
   public final static Name NM_ADMIN = Name.create(NAMESPACE,"admin");

   public static class Interface {
      InetAddress address;
      int secure;
      int insecure;
      
      public Interface(InetAddress addr,int insecure,int secure)
      {
         this.address = addr;
         this.insecure = insecure;
         this.secure = secure;
      }
      
      public int getPort()
      {
         return insecure;
      }
      
      public void setPort(int port)
      {
         this.insecure = port;
      }

      public int getSecurePort()
      {
         return secure;
      }
      
      public void setSecurePort(int port)
      {
         this.secure = port;
      }

      public InetAddress getAddress()
      {
         return address;
      }

      public void setAddress(InetAddress address)
      {
         this.address = address;
      }

   }
   
   public static class Host {
      String name;
      URI authService;
      
      public Host(String name,URI authService) {
         this.name = name;
         this.authService = authService;
      }
      public String getName() {
         return name;
      }
      public URI getAuthService() {
         return authService;
      }
   }
   
   public static class Link {
      URI uri;
      String username;
      String password;
      public Link(URI uri,String username,String password)
      {
         this.uri = uri;
         this.username = username;
         this.password = password;
      }
      
      public URI getURI() {
         return uri;
      }
      
      public String getUsername() {
         return username;
      }
      
      public String getPassword() {
         return password;
      }
   }
   
   
   long autoconfCheck;
   Map<String,Host> hosts;
   List<Interface> interfaces;
   File keystoreFile;
   String keystorePassword;
   String keyPassword;
   List<Link> autoconfs;
   URI databaseRef;
   String adminHost;
   int adminPort;
   URI adminAuthService;
   
   /** Creates a new instance of ServerConfiguration */
   public ServerConfiguration()
   {
      autoconfCheck = 180*1000;
      autoconfs = new ArrayList<Link>();
      hosts = new TreeMap<String,Host>();
      interfaces = new ArrayList<Interface>();
   }
   
   public URI getDatabaseRef() {
      return databaseRef;
   }
   
   public long getAutoConfigurationCheckWait() {
      return autoconfCheck;
   }
   
   public void setAutoConfigurationCheckWait(long value) {
      autoconfCheck = value;
   }
   
   public List<Link> getAutoConfiguration() {
      return autoconfs;
   }
   
   public File getKeystoreFile() {
      return keystoreFile;
   }
   
   public void setKeystoreFile(File f)
   {
      keystoreFile = f;
   }
   
   public String getKeystorePassword() {
      return keystorePassword;
   }
   
   public void setKeystorePassword(String password)
   {
      keystorePassword = password;
   }
   
   public String getKeyPassword() {
      return keyPassword;
   }
   
   public void setKeyPassword(String password)
   {
      keyPassword = password;
   }
   
   public String getAdminHost()
   {
      return adminHost;
   }
   
   public void setAdminHost(String host) {
      adminHost = host;
   }
   
   public int getAdminPort() {
      return adminPort;
   }
   
   public void setAdminPort(int port)
   {
      adminPort = port;
   }
   
   public void setAdminAuthService(URI service) {
      adminAuthService = service;
   }
   
   public URI getAdminAuthService()
   {
      return adminAuthService;
   }
   
   
   public void load(URI location)
      throws IOException,XMLException
   {
      DocumentLoader loader = new SAXDocumentLoader();
      Document doc = loader.load(location);
      Element top = doc.getDocumentElement();
      if (!top.getName().equals(NM_SERVER)) {
         throw new XMLException("Expecting "+NM_SERVER+" but found "+top.getName());
      }
      String value = top.getAttributeValue("autoconf-check");
      if (value!=null) {
         autoconfCheck = Long.parseLong(value)*1000;
      }
      
      Element adminE = top.getFirstElementNamed(NM_ADMIN);
      if (adminE!=null) {
         adminHost = adminE.getAttributeValue("address");
         String portS = adminE.getAttributeValue("port");
         adminPort = portS==null ? 8082 : Integer.parseInt(portS);
         String href = adminE.getAttributeValue("auth");
         if (href==null) {
            throw new XMLException("The 'auth' attribute is missing on the admin.",(Location)adminE);
         }
         adminAuthService = adminE.getBaseURI().resolve(href);
      }
      
      Element databaseE = top.getFirstElementNamed(NM_DATABASE);
      if (databaseE==null) {
         throw new XMLException("Missing database configuration.");
      }
      databaseRef = databaseE.getBaseURI().resolve(databaseE.getAttributeValue("href"));
      Iterator<Element> autoconfElements = top.getElementsByName(NM_AUTOCONF);
      while (autoconfElements.hasNext()) {
         Element autoconfE = autoconfElements.next();
         String href = autoconfE.getAttributeValue("href");
         if (href!=null) {
            Link l = new Link(autoconfE.getBaseURI().resolve(href),autoconfE.getAttributeValue("username"),autoconfE.getAttributeValue("password"));
            autoconfs.add(l);
         }
      }
      Iterator<Element> interfaceElements = top.getElementsByName(NM_INTERFACE);
      while (interfaceElements.hasNext()) {
         Element interfaceE = interfaceElements.next();
         String addr = interfaceE.getAttributeValue("address");
         InetAddress iaddr = addr==null ? null : (addr.equals("*") ? null : InetAddress.getByName(addr));
         String portS = interfaceE.getAttributeValue("secure");
         int secure = portS==null ? 5223 : Integer.parseInt(portS);
         portS = interfaceE.getAttributeValue("insecure");
         int insecure = portS==null ? 5222 : Integer.parseInt(portS);
         interfaces.add(new Interface(iaddr,insecure,secure));
      }
      
      Iterator<Element> hostElements = top.getElementsByName(NM_HOST);
      while (hostElements.hasNext()) {
         Host host = createHost(hostElements.next());
         String key = host.getName();
         hosts.put(key,host);
      }
      
      Element keystoreE = top.getFirstElementNamed(NM_KEYSTORE);
      if (keystoreE!=null) {
         String href = keystoreE.getAttributeValue("href");
         URI keystoreRef = keystoreE.getBaseURI().resolve(href);
         keystoreFile = new File(keystoreRef.getSchemeSpecificPart());
         keystorePassword = keystoreE.getAttributeValue("password");
         keyPassword = keystorePassword;
         String altPassword = keystoreE.getAttributeValue("key-password");
         if (altPassword!=null) {
            keyPassword = altPassword;
         }
      }
   }
   
   public Host createHost(Element hostE)
      throws XMLException
   {
      String name = hostE.getAttributeValue("name");
      String href = hostE.getAttributeValue("auth");
      if (name==null) {
         throw new XMLException("The 'name' attribute is missing on the host.",(Location)hostE);
      }
      if (href==null) {
         throw new XMLException("The 'auth' attribute is missing on the host.",(Location)hostE);
      }
      URI authService = hostE.getBaseURI().resolve(href);
      Host host = new Host(name,authService);
      return host;
   }
   
   public Map<String,Host> getHosts() {
      return hosts;
   }
   
   public List<Interface> getInterfaces() {
      return interfaces;
   }
   
   public void store(URI baseURI,ItemDestination dest)
      throws XMLException
   {
      String baseURIValue = baseURI.toString();
      URI baseDir = URI.create(baseURIValue.substring(0,baseURIValue.lastIndexOf('/')+1));
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      dest.send(constructor.createDocument(baseURI));
      Element top = constructor.createElement(NM_SERVER);
      if (autoconfCheck!=((long)(180*1000))) {
         top.setAttributeValue("autoconf-check",Long.toString(autoconfCheck/1000));
      }
      dest.send(top);
      dest.send(constructor.createCharacters("\n"));
      
      Element keystoreE = constructor.createElement(NM_KEYSTORE);
      keystoreE.setAttributeValue("href",baseDir.relativize(keystoreFile.toURI()).toString());
      keystoreE.setAttributeValue("password",keystorePassword);
      if (!keystorePassword.equals(keyPassword)) {
         keystoreE.setAttributeValue("key-password",keyPassword);
      }
      dest.send(keystoreE);
      dest.send(constructor.createElementEnd(NM_KEYSTORE));
      dest.send(constructor.createCharacters("\n"));
      
      if (adminHost!=null) {
         Element adminE = constructor.createElement(NM_ADMIN);
         adminE.setAttributeValue("address", adminHost);
         adminE.setAttributeValue("port", Integer.toString(adminPort));
         dest.send(adminE);
         dest.send(constructor.createElementEnd(NM_ADMIN));
      }
      
      for (Link link : autoconfs) {
         Element autoconfE = constructor.createElement(NM_AUTOCONF);
         autoconfE.setAttributeValue("href",baseURI.relativize(link.getURI()).toString());
         if (link.getUsername()!=null) {
            autoconfE.setAttributeValue("username",link.getUsername());
            autoconfE.setAttributeValue("password",link.getPassword());
         }
         dest.send(autoconfE);
         dest.send(constructor.createElementEnd(NM_AUTOCONF));
         dest.send(constructor.createCharacters("\n"));
      }
      
      for (Interface iface : interfaces) {

         Element ifaceE = constructor.createElement(NM_INTERFACE);
         ifaceE.setAttributeValue("address",iface.getAddress().toString());
         ifaceE.setAttributeValue("insecure",Integer.toString(iface.getPort()));
         ifaceE.setAttributeValue("secure",Integer.toString(iface.getSecurePort()));
         dest.send(ifaceE);
         dest.send(constructor.createElementEnd(NM_INTERFACE));
         dest.send(constructor.createCharacters("\n"));
      }

      for (Host host : hosts.values()) {
         
         Element hostE = constructor.createElement(NM_HOST);
         hostE.setAttributeValue("name",host.getName());
         hostE.setAttributeValue("auth",baseURI.relativize(host.getAuthService()).toString());
         dest.send(hostE);
         dest.send(constructor.createElementEnd(NM_HOST));
         dest.send(constructor.createCharacters("\n"));
      }
      
      dest.send(constructor.createElementEnd(NM_SERVER));
      dest.send(constructor.createDocumentEnd());
   }
   
}
